package p18_统一转为环绕通知;

import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import p17_从advisor到aspect.MyAspect;
import p17_从advisor到aspect._3_高级切面转换成低级切面;

import java.lang.reflect.Method;
import java.util.List;

/**
 * 我们知道，aop的通知类型有很多，前置、后置、异常...
 * 这些不同类型的通知在执行前，都会转为环绕通知(MethodInterceptor)，与目标方法组成一个调用链对象(MethodInvocation)，代理类的回调函数内，执行的就是这个调用链对象
 */
public class _1_ToAround {
    public static void main(String[] args) throws Throwable {
        /**
         * @Before会被转换成原始的AspectJMethodBeforeAdvice形式，该对象包含:
         *  1.通知代码从哪来 -> method
         *  2.切点是什么 -> pt
         *  3.通知对象如何创建 -> factory
         * 类似的通知还有
         *  1.AspectJAroundAdvice   -> 环绕通知
         *  2.AspectJAfterReturningAdvice  -> return通知
         *  3.AspectJAfterThrowingAdvice ->异常通知(实现了MethodInterceptor,认为是环绕通知，无需转换)
         *  4.AspectJAfterAdvice    ->after通知(实现了MethodInterceptor,认为是环绕通知，无需转换)
         * 以上通知，继承了MethodInterceptor就可以认为是环绕通知
         *
         * 无论什么通知，最后都会统一转换成环绕通知(实现了MethodInterceptor接口)
         * 多个前置通知与后置通知与目标方法会生成一条职责链，因此需要一个调用链对象，即MethodInvocation，最后干活的就是这个对象
         * 调用顺序如下(会将MethodInvocation放入当前线程，将来任何一个通知都可以得到调用链对象，存放MethodInvocation的操作由一个最外层的环绕通知来实现)
         * | -> before1 -------------------------------
         * |  -> before2 -----------------            |
         * |    -> target ------- 目标    |advice2    advice1
         * |  -> after2  -----------------            |
         * | -> after1  ------------------------------
         * 类似递归的方式，因此使用环绕通知最佳，其他通知(AspectJMethodBeforeAdvice、AspectJAfterReturningAdvice)转为环绕通知使用的是适配器模式
         * 如果已经是环绕通知，就不转换了
         */
        //高级切面 -> 低级切面
        List<Advisor> list = new _3_高级切面转换成低级切面.Aspect2Advisor().convert(new MyAspect());
        //创建代理的工厂
        Target target = new Target();
        ProxyFactory f = new ProxyFactory();
        f.setTarget(target);
        //将收集到的所有通知赋给代理工厂
        f.addAdvisors(list);
        /**
         * 调用该方法，就能将所有通知转为环绕通知，并返回环绕通知集合，该环绕通知配合目标，就能形成职责链
         * 参数:目标方法对象，目标方法类型
         * 统一转换为环绕通知，体现的是适配器模式
         *  1.将@Before AspectJMethodBeforeAdvice适配为MethodBeforeAdviceInterceptor
         *  2.将AspectJAfterReturningAdvice适配为AfterReturningAdviceInterceptor
         * 是如何转换的呢？
         *  MethodBeforeAdviceAdapter：该适配器将 MethodBeforeAdvice -> MethodBeforeAdviceInterceptor
         *  AfterReturningAdviceAdapter：该适配器将 AfterReturningAdvice -> AfterReturningAdviceInterceptor
         */
        //这是一个环绕通知集合(实际解析时，会根据order来为advisor进行排序)
        List<Object> methodInterceptorList = f.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo"), Target.class);
        //在最外层设置一个环绕通知，这个通知负责把MethodInvocation放入当前线程
        methodInterceptorList.add(0, ExposeInvocationInterceptor.INSTANCE);
        System.out.println(methodInterceptorList);

        /**
         * 将环绕通知集合 + 目标方法封装成调用链
         * ReflectiveMethodInvocation的构造器是受保护的，所以这里使用自定义的MyReflectiveMethodInvocation继承ReflectiveMethodInvocation
         */
        MethodInvocation methodInvocation = new MyReflectiveMethodInvocation(null, target, Target.class.getMethod("foo"), new Object[0], Target.class, methodInterceptorList);
        //执行调用链，发现目标方法被增强了
        methodInvocation.proceed();

    }

    static class MyReflectiveMethodInvocation extends ReflectiveMethodInvocation {
        protected MyReflectiveMethodInvocation(Object proxy, Object target, Method method, Object[] arguments, Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
            super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
        }
    }
}
